home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1995 November
/
EnigmA AMIGA RUN 02 (1995)(G.R. Edizioni)(IT)[!][issue 1995-11][Skylink CD].iso
/
docs
/
corsoguide
/
liste-6.txt
< prev
next >
Wrap
Text File
|
1992-09-03
|
7KB
|
162 lines
Liste e nodi
Il sistema operativo mantiene durante il suo periodo di attività un elevato
numero di liste di dati di vario tipo: schermi, finestre, blocchi memoria,
task, interrupt ecc. Dato che tutte queste liste hanno un numero di elementi
variabile, vengono realizzate mediante strutture linkate; ciò significa che
oltre ai dati di un elemento della lista viene inserito un indirizzo che
costituisce il puntatore al prossimo elemento in modo da costituire una
catena. Exec provvede fornendo un implementazione standard per tutte le
liste che gestisce, mettendo a disposizione quindi le routine per ricerca,
inserimento e altre che non gravano così sul programmatore; la struttura
linkata realizzata con exec viene denominata coda (in inglese queue)
bidirezionale, cioè ogni elemento oltre ad avere il puntatore all'elemento
successivo ha anche il puntatore a quello precedente; in più vi sono due
elementi particolari, Head node e Tail node che puntano rispettivamente al
primo e all'ultimo elemento della lista e necessitano per iniziare una
qualsiasi procedura di ricerca. In termini pratici tutto ciò viene tradotto
tramite una struttura:
struct Node
{
struct Node *ln_Succ;
struct
Node *ln_Prec;
UBYTE ln_Type;
BYTE ln_Pri;
char *ln_Name;
};
Come vedete la struttura Node non solo è costituita dai puntatori al
nodo successivo e precedente ma anche da altri dati: 'ln_Type' indica il
tipo del nodo ed assume diverse definizioni a seconda dell'ambiente in cui
viene utilizzato che specificheremo volta per volta; 'ln_Pri' indica la
priorità dell'elemento (nella lista di task indica chi ha la precedenza
sugli altri) e per questo molte volte influisce sull'ordinamento della
lista (vale a dire viene prima l'elemento con priorità più alta) di solito
'ln_Pri' assume 0; 'ln_Name' è il puntatore ad una stringa che identifica
univocamente l'elemento e può essere utilizzato per la ricerca; questi tre
elementi sono opzionali e vedremo volta per volta quando vengono
utilizzati. Questa struttura deve essere definita all'inizio dell'elemento
dati della lista; ad esempio:
struct Persona
{
struct Node LinkNode;
char *Nome;
char *Cognome;
int anni;
char *codicefis;
};
E' possibile comunque utilizzare una struttura "minima" e cioè senza
ln_Type, ln_Pri, ln_Name nella quale non fossero necessarie, denominata
MinNode:
struct MinNode
{
struct MinNode *min_Succ;
struct MinNode *min_Pred;
};
Occorre mantenere anche le informazioni riguardanti l'indirizzo del nodo
di testa e di quello di coda, per cui si utilizzerà la struttura List:
struct List
{
struct Node *lh_Head;
struct Node *lh_Tail;
struct Node *lh_TailPred;
UBYTE lh_Type;
UBYTE lh_Pad;
};
lh_Type è un codice numerico che indica il tipo di lista (o meglio qual è
il contenuto di codesta); lh_Pad non contiene alcun informazione e serve
solamente ad assicurare l'allineamento a word di dati eventualmente presenti
dopo; i primi tre valori sono puntatori a nodi e rappresentano la testa e la
coda della lista; tale modo di conservare i puntatori non è quello
convenzionale in quanto normalmente, si considerano i puntatori agli
elementi di testa e di coda e si devono gestire una serie di casi
particolari (inserimento in testa, inserimento in una lista vuota ecc.)
implementando così una serie di verifiche di sicurezza; in questa struttura
invece sono direttamente presenti gli elementi di testa e di coda da
considerare "immaginari" perché non contengono alcun dato; in termini
pratici lh_Head ed lh_Tail rappresentano rispettivamente il puntatore
all'elemento successivo e all'elemento precedente di questo nodo di testa
immaginario, ed il primo è il puntatore al primo nodo effettivo della
struttura dati, ed il secondo vale 0; mentre lh_Tail e lh_TailPred
costituiscono il successivo e il precedente del nodo di coda immaginario e
valgono 0 per il primo e l'indirizzo dell'ultimo nodo effettivo per il
secondo (lh_Tail è condiviso perché vale sempre 0); capite che facendo così
si eliminano tutti i casi particolari e con essi le verifiche necessarie per
implementarli; infatti in caso di lista vuota esistono comunque due elementi
(la testa e la coda immaginari), per cui l'inserimento in una lista vuota è
uguale all'inserimento normale, oppure l'inserimento di un nodo in testa
alla lista equivale realmente ad un inserimento qualunque, poiché il nodo di
testa della lista si trova sempre dopo quello immaginario è quindi non è
realmente in testa anche se al programmatore sembra che sia così.
Dopo questa spiegazione da cane che si morde la coda, per inizializzare una
lista e gestirla vi sono comunque delle funzioni di exec apposite e che
quindi non richiedono nessuna operazione aggiuntiva da parte del
programmatore.
struct MinList
{
struct MinNode *mlh_Head;
struct MinNode *mlh_Tail;
struct MinNode *mlh_TailPred;
};
questa struttura viene utilizzata nella stessa maniera di List salvo che per
questa non vanno specificate le informazioni aggiuntive. Osserviamo ora le
funzioni messe a disposizione da Exec per la gestione delle liste:
NewList(Lista);
dove Lista è il puntatore ad una struttura List; NewList effettua
l'inizializzazione della lista vuota ed imposta correttamente i valori di
Lista.
AddHead(Lista,Nodo);
AddTail(Lista,Nodo);
Enqueue(Lista,Nodo);
dove Lista è sempre il puntatore ad una struttura List (se è appena creata
occorre inizializzarla con NewList) e Nodo è il puntatore alla struttura
Node da inserire nella lista; AddHead serve per inserire il nodo in testa
alla lista, AddTail per inserirlo in fondo ed Enqueue per inserire il nodo
in modo da mantenere la lista ordinata per priorità (ln_Pri); tenete
presente che Enqueue deve operare su una lista già ordinata (vale a dire
ogni elemento della lista deve essere stato inserito con questa funzione,
oppure facendo attenzione che la posizione a seconda della priorità sia
giusta); con Enqueue, il nodo verrà inserito dopo l'ultimo elemento con
priorità maggiore o uguale a quella sua ed inoltre, il primo elemento della
lista (quello di testa) ha priorità maggiore (quindi ordinamento numerico
decrescente); questa funzione non può essere utilizzata ovviamente con liste
minime.
Insert(Lista,Nodo,NodoPrec);
dove Lista e Nodo hanno lo stesso significato di prima e NodoPrec è il
puntatore ad un nodo della lista "Lista"; Insert inserirà il nodo "Nodo"
nella lista "Lista" nella posizione immediatamente successiva a NodoPrec.
Remove(Nodo);
Nodo = (struct Node *)RemHead(Lista);
Nodo = (struct Node *)RemTail(Lista);
Remove rimuoverà il nodo "Nodo" dalla lista in cui è presente; quì non
occorre specificare nessuna lista giacché in Nodo stesso sono presente tutte
le informazioni per la sua eliminazione (puntatore al nodo precedente ad a
quello successivo); RemHead e RemTail rimuovono rispettivamente il nodo di
testa e quello di coda e ne ritornano l'indirizzo in caso serva (per la sua
deallocazione o per altri usi).
Nodo = (struct Node *)FindName(Lista,nome);
dove nome è il puntatore ad una stringa; FindName ricercherà il nodo con
nome (ln_Name) "nome" nella lista "Lista" e se lo troverà ritornerà il suo
indirizzo altrimenti ritornerà NULL; questa funzione non può essere
utilizzata con le liste minime.